package org.whuims.pois.dao;

import org.apache.logging.log4j.core.helpers.Strings;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser.Operator;
import org.apache.lucene.search.*;
import org.apache.lucene.search.highlight.*;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.whuims.pois.action.result.DataVo;
import org.whuims.pois.action.result.SearchBean;
import org.whuims.pois.action.result.SearchResult;
import org.whuims.pois.common.TimeUtils;
import org.wltea.analyzer.lucene.IKAnalyzer;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.text.SimpleDateFormat;
import java.util.*;

public class SearchDao {
    private static String INDEX_PATH = "/work/leishengwei/poisdata/index/serviceIndex";
//    private static String INDEX_PATH = "D:/index/serviceIndex";
//    private static String INDEX_PATH = "F:/Data/Data/htts/index/serviceIndex";

    private Analyzer analyzer = new IKAnalyzer();

    public SearchResult search(SearchBean searchBean, HttpServletRequest request, int page, int size) {
        if (searchBean.getSt() == 0) {
            searchBean.setTitle("");
            searchBean.setSite("");
            searchBean.setType("");
            if (Strings.isEmpty(searchBean.getKeyword())) {
                return new SearchResult(0, 0);
            }
            HttpSession session = request.getSession();
            long topicId = (Long)session.getAttribute("topicId");
            return search(searchBean.getKeyword(), topicId, page, size);
        }
        if (Strings.isEmpty(searchBean.getSite()) && Strings.isEmpty(searchBean.getTitle())) {
            return new SearchResult(0, 0);
        }
        searchBean.setKeyword("");
        Query query = parseQuery(searchBean);
        return search(query, page, size);
    }

    public SearchResult search(String keyword, long topicId, int page, int size) {
        Query keywordQuery = parseQuery(keyword);
        BooleanQuery booleanQuery = new BooleanQuery();
        Query topicQuery = null;
        if (topicId!=0){
            String topicString = String.valueOf(topicId);
            topicQuery = new TermQuery(new Term("topicId", topicString));
        }
        booleanQuery.add(keywordQuery, BooleanClause.Occur.MUST);
        if (topicQuery != null)
            booleanQuery.add(topicQuery, BooleanClause.Occur.MUST);
        return  search(booleanQuery, page, size);
    }

    public SearchResult search(Query query, int page, int size) {
        if (query == null) {
            return new SearchResult(0, 0);
        }
        List<Document> result = new ArrayList<Document>();
        System.out.println("SearchDao Query: " + query);
        try {
            IndexSearcher searcher = new IndexSearcher(FSDirectory
                    .open(new File(INDEX_PATH)), true);

            TopScoreDocCollector scoreDocCollector = TopScoreDocCollector.create(
                    760, true);
            long time = new Date().getTime();
            searcher.search(query, null, scoreDocCollector);
            double useTime = (new Date().getTime() - time) / 1000.0;
            SimpleHTMLFormatter simpleHTMLFormatter = new SimpleHTMLFormatter("<span style='color:red'>", "</span>");
            Highlighter highlighter = new Highlighter(simpleHTMLFormatter, new QueryScorer(query));
            int count = scoreDocCollector.getTotalHits();
            ScoreDoc[] topDocs = scoreDocCollector.topDocs(page * size, size).scoreDocs;
            for (int i = 0; i < topDocs.length; i++) {
                result.add(createObj(searcher, topDocs[i]));
            }
            List<DataVo> list = toDataVo(result, highlighter);
            return new SearchResult(useTime, count, list);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return new SearchResult(0, 0);
    }

    private Document createObj(IndexSearcher searcher, ScoreDoc topDoc) throws IOException {
        return searcher.doc(topDoc.doc);
    }

    private List<DataVo> toDataVo(List<Document> result, Highlighter highlighter) {
        List<DataVo> list = new ArrayList<DataVo>();
        if (result == null) {
            return new ArrayList<DataVo>();
        }
        Iterator<Document> iterator = result.iterator();
        while (iterator.hasNext()) {
            Document d = iterator.next();
            final DataVo vo = toDataVo(d, highlighter);
            if (vo != null) {
                list.add(vo);
            }
        }
        return list;
    }


    /**
     * 将{@link org.apache.lucene.document.Document}转换为{@link org.whuims.pois.action.result.DataVo}对象
     *
     * @param d
     * @return
     */
    private DataVo toDataVo(Document d, Highlighter highlighter) {
        DataVo dataVo = new DataVo();
        String idStr = d.get("id");
        if (Strings.isEmpty(idStr)) {
            return null;
        }
        Long id;
        try {
            id = Long.parseLong(idStr);
        } catch (NumberFormatException e) {
            return null;
        }
        dataVo.setId(id);
        dataVo.setLink(d.get("titleLink"));
        dataVo.setTitle(highlight(d, highlighter, "titleContent"));
        dataVo.setAbstext(highlight(d, highlighter, "absText"));
        dataVo.setSite(highlight(d, highlighter, "sitename"));
        final String newstime = d.get("newsTime");
        if (Strings.isNotEmpty(newstime)) {
            dataVo.setTime(TimeUtils.str2Date(newstime, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")));
        }
        dataVo.setdType(d.get("type"));
        return dataVo;
    }

    private String highlight(Document d, Highlighter highlighter, String fieldName) {
        String title = d.get(fieldName).trim();
        if (Strings.isNotEmpty(title)) {
            highlighter.setTextFragmenter(new SimpleFragmenter(1000));
            TokenStream tk = analyzer.tokenStream(fieldName, new StringReader(title));
            String htext = null;
            try {
                htext = highlighter.getBestFragment(tk, title);
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InvalidTokenOffsetsException e) {
                e.printStackTrace();
            }
            if (htext == null) {
                return title;
            }

            return htext;
        }
        return null;
    }



    /**
     * 将{@link org.whuims.pois.action.result.SearchBean} 解析为lucene的{@link org.apache.lucene.search.Query}
     *
     * @param searchBean
     * @return
     */
    private Query parseQuery(SearchBean searchBean) {
        if (Strings.isEmpty(searchBean.getTitle())) {
            return null;
        }
        Query titleQuery = parseQuery(searchBean.getTitle());
        Query siteQuery = null;
        if (Strings.isNotEmpty(searchBean.getSite())) {
            siteQuery = new FuzzyQuery(new Term("sitename", searchBean.getSite()));
        }
        Query typeQuery = null;
        if (!searchBean.getType().equals("all")) {
            typeQuery = new TermQuery(new Term("type", searchBean.getType()));
        }

        Query topicQuery = null;
        if (searchBean.getTopic()!=0){
            String topicidString = String.valueOf(searchBean.getTopic());
            topicQuery = new TermQuery(new Term("topicId", topicidString));
        }

        BooleanQuery booleanQuery = new BooleanQuery();
        booleanQuery.add(titleQuery, BooleanClause.Occur.MUST);
        if (siteQuery != null) {
            booleanQuery.add(siteQuery, BooleanClause.Occur.SHOULD);
        }
        if (typeQuery != null) {
            booleanQuery.add(typeQuery, BooleanClause.Occur.MUST);
        }
        if(topicQuery != null){
            booleanQuery.add(topicQuery, BooleanClause.Occur.MUST);
        }

        return booleanQuery;
    }

    /**
     * 将检索关键词解析为Query对象
     */
    private Query parseQuery(String keyword) {
        String[] fields = {"titleContent", "absText", "mainContent"};
        /** 设置不同域检索结果的权重 */
        Map<String, Float> map = new HashMap<String, Float>();
        map.put("titleContent", 1.0f);
        map.put("absText", 0.6f);
        map.put("mainContent", 0.4f);
        try {
            MultiFieldQueryParser parser = new MultiFieldQueryParser(Version.LUCENE_30, fields, analyzer, map);
            parser.setDefaultOperator(Operator.OR);
            Query query = parser.parse(keyword);
            return query;
        } catch (ParseException e) {
            e.printStackTrace();
            return null;
        }
    }
}
